//
//  YYNavigationController.m
//  YYNavigationBarExample
//
//  Created by yangyang on 2016/11/11.
//  Copyright © 2016年 机智的静默( http://www.cnblogs.com/jingmo/ ). All rights reserved.
//

#import "YYNavigationController.h"
#import "YYNavigationBar.h"
#import "YYNavigationItem.h"
#import "YYNavigationPushAnimation.h"
#import "YYNavigationPopAnimation.h"
#import "YYNavigationMacro.h"
#import "UIViewController+YYNavigationView.h"
#import "NSObject+YYHelper.h"

typedef NS_ENUM(NSUInteger, YYNavigationBackType) {
    kYYNavigationBackTypeButton = 0,
    kYYNavigationBackTypeGesture
};

@interface YYNavigationController () <UINavigationControllerDelegate, UIGestureRecognizerDelegate>

@property (nonatomic) NSMutableArray *navigationBarStack;
@property (nonatomic) NSMutableArray *vcArray;

@property (nonatomic) YYNavigationBar *previousBar;
@property (nonatomic) CGFloat previousBarInitialNaviAlpha;
@property (nonatomic) CGFloat naviBarInitialNaviAlpha;

@property (nonatomic) YYNavigationBackType backType;
@property (nonatomic) UIScreenEdgePanGestureRecognizer *screenEdgeGesture;
@property (nonatomic) UIPanGestureRecognizer *panGesture;
@property (nonatomic) UIPercentDrivenInteractiveTransition *interactivePopTransition;

@end

@implementation YYNavigationController

#pragma mark - LifeCycle

- (instancetype)initWithRootViewController:(UIViewController *)rootViewController gestureType:(YYNavigationGestureType)gestureType {
    self = [super initWithRootViewController:rootViewController];
    if (self) {
        [self setupCustomNavigationBar:NO];
        self.gestureType = gestureType;
    }
    return self;
}

- (instancetype)initWithRootViewController:(UIViewController *)rootViewController {
    return [self initWithRootViewController:rootViewController gestureType:kYYNavigationGestureScreenEdgeType];
}

- (void)awakeFromNib {
    
    [super awakeFromNib];
    [self setupCustomNavigationBar:YES];
    [self setGestureType:kYYNavigationGestureScreenEdgeType];
}

#pragma mark - Setup

- (void)setupCustomNavigationBar:(BOOL)isXib {
    // present时全屏
    self.modalPresentationStyle = UIModalPresentationFullScreen;
    self.navigationBar.hidden = YES;
    self.interactivePopGestureRecognizer.enabled = NO;
    // initWithRoot时，会自动走pushViewController
    // xib不会走
    if (isXib) {
        [self.vcArray addObjectsFromArray:self.viewControllers];
        [self addNavigationBarWithVC:self.vcArray.firstObject stackCount:self.vcArray.count];
        [self showNavigationBar:self.vcArray.lastObject];
    }
}

#pragma mark - Status Bar

- (BOOL)prefersStatusBarHidden {
    return [self.topViewController prefersStatusBarHidden];
}

- (UIStatusBarStyle)preferredStatusBarStyle {
    return [self.topViewController preferredStatusBarStyle];
}

#pragma mark - ModifyNavigationBar

- (void)addNavigationBarWithVC:(UIViewController *)vc stackCount:(NSUInteger)stackCount {
    if (!vc.yy_navigationBar) {
        YYNavigationBar *navigationBar = [YYNavigationBar naviBarWithStackCount:stackCount lastViewController:vc];
        [self.navigationBarStack addObject:navigationBar];
        [vc setValue:navigationBar forKey:@"yy_navigationBar"];
        [vc setValue:navigationBar.yy_navigationItem forKey:@"yy_navigationItem"];
        [vc setValue:self forKey:@"yy_navigationController"];
    }
}

- (void)removeNavigationBarWithVCs:(NSArray <UIViewController *> *)vcs {
    [vcs enumerateObjectsUsingBlock:^(UIViewController * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        if (obj.yy_navigationBar) {
            [self.navigationBarStack removeObject:obj.yy_navigationBar];
            [obj setValue:nil forKey:@"yy_navigationBar"];
            [obj setValue:nil forKey:@"yy_navigationItem"];
        }
    }];
}

- (void)showNavigationBar:(UIViewController *)lastVC {
    if (self.yy_navigationBar.superview) {
        [self.yy_navigationBar removeFromSuperview];
    }
    
    [self setValue:self.navigationBarStack.lastObject forKey:@"yy_navigationBar"] ;
    [self setValue:self.yy_navigationBar.yy_navigationItem forKey:@"yy_navigationItem"];
    [self.view addSubview:self.yy_navigationBar];
    
    [lastVC setValue:self.yy_navigationBar forKey:@"yy_navigationBar"];
    [lastVC setValue:self.yy_navigationItem forKey:@"yy_navigationItem"];
    [lastVC setValue:self forKey:@"yy_navigationController"];
}

#pragma mark - ReplaceSuperMethod

/// initWithRoot会正常执行；
/// 单一push会正常执行；
/// 连续push会执行次数为push次数+1，且最后一个界面会在动画结束后才能添加到栈中
- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated {
    [super pushViewController:viewController animated:animated];
    // 如果老的栈中不包含新界面，则进行添加
    if (![self.vcArray containsObject:viewController]) {
        [self.vcArray addObject:viewController];
        [self addNavigationBarWithVC:viewController stackCount:self.vcArray.count];
        [self showNavigationBar:viewController];
    }
}

/// 这里为什么不使用系统返回的数组，是因为在连续push/pop的时候系统会返回空
/// 当有动画时，[super popToRootViewControllerAnimated] 执行完，self.viewControllers栈里面会有首和尾
/// 以及其他异常
- (NSArray<UIViewController *> *)popToRootViewControllerAnimated:(BOOL)animated {
    if (self.vcArray.count < 2) {
        return nil;
    }
    UIViewController *showVC = [self.vcArray firstObject];
    NSArray *removeVC = [self.vcArray subarrayWithRange:NSMakeRange(1, self.vcArray.count - 1)];
    [self.vcArray removeObjectsInArray:removeVC];
    [super popToRootViewControllerAnimated:animated];
    [self removeNavigationBarWithVCs:removeVC];
    [self showNavigationBar:showVC];
    return removeVC;
}

/// 这里为什么不使用系统返回的数组，是因为在连续push/pop的时候系统会返回空
/// 当有动画时，[super popToViewController] 执行完，self.viewControllers栈里面会有首和尾
/// 以及其他异常
- (NSArray<UIViewController *> *)popToViewController:(UIViewController *)viewController animated:(BOOL)animated {
    if (self.vcArray.count < 2) {
        return nil;
    }
    
    NSUInteger index = [self.vcArray indexOfObject:viewController];
    if (index == NSNotFound) {
        return nil;
    }
    
    NSMutableArray *removeVC = [NSMutableArray array];
    [self.vcArray enumerateObjectsUsingBlock:^(__kindof UIViewController * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        if (idx > index) {
            [removeVC addObject:obj];
        }
    }];
    [self.vcArray removeObjectsInArray:removeVC];
    [super popToViewController:viewController animated:animated];
    [self removeNavigationBarWithVCs:removeVC];
    [self showNavigationBar:viewController];
    return removeVC;
}

- (UIViewController *)popViewControllerAnimated:(BOOL)animated {
    if (self.vcArray.count < 2) {
        return nil;
    }
    UIViewController *removeVC = self.vcArray.lastObject;
    UIViewController *showVC = [self.vcArray objectAtIndex:self.vcArray.count - 2];
    [super popViewControllerAnimated:animated];
    if (self.backType == kYYNavigationBackTypeButton) {
        [self.vcArray removeObject:removeVC];
        [self removeNavigationBarWithVCs:@[removeVC]];
        [self showNavigationBar:showVC];
    }
    return removeVC;
}

- (void)setViewControllers:(NSArray<__kindof UIViewController *> *)viewControllers {
    [self setViewControllers:viewControllers animated:NO];
}

- (void)setViewControllers:(NSArray<UIViewController *> *)viewControllers animated:(BOOL)animated {
    // 获取现在栈中所有VC
    NSArray *oldVCs = self.vcArray;
    // 获取所有需要删除的VC
    NSMutableArray *deleteArray = [NSMutableArray array];
    [oldVCs enumerateObjectsUsingBlock:^(UIViewController * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        if (![viewControllers containsObject:obj]) {
            [deleteArray addObject:obj];
        }
    }];
    [self.vcArray removeObjectsInArray:deleteArray];
    // 删除vc的导航栏
    [self removeNavigationBarWithVCs:deleteArray];
    
    [super setViewControllers:viewControllers animated:animated];
    
    // 给新的VC增加导航栏
    [viewControllers enumerateObjectsUsingBlock:^(UIViewController * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        if (![oldVCs containsObject:obj]) {
            [self.vcArray addObject:obj];
            [self addNavigationBarWithVC:obj stackCount:self.vcArray.count];
        }
    }];
    [self showNavigationBar:viewControllers.lastObject];
}

#pragma mark - NavigationControllerDelegate

- (id <UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController
                                   animationControllerForOperation:(UINavigationControllerOperation)operation
                                                fromViewController:(UIViewController *)fromVC
                                                  toViewController:(UIViewController *)toVC {
    if (operation == UINavigationControllerOperationPop) {
        return [[YYNavigationPopAnimation alloc] init];
    } else if (operation == UINavigationControllerOperationPush) {
        YYNavigationPushAnimation *pushAnimation = [YYNavigationPushAnimation new];
        self.interactivePopGestureRecognizer.view.userInteractionEnabled = NO;
        @weakify(self);
        pushAnimation.animationEndBlock = ^{
            @strongify(self);
            self.interactivePopGestureRecognizer.view.userInteractionEnabled = YES;
        };
        return pushAnimation;
    }
    return nil;
}

- (id <UIViewControllerInteractiveTransitioning>)navigationController:(UINavigationController *)navigationController interactionControllerForAnimationController:(id<UIViewControllerAnimatedTransitioning>)animationController {
    if ([animationController isKindOfClass:[YYNavigationPopAnimation class]]) {
        return self.interactivePopTransition;
    }
    return nil;
}

- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated {
    viewController.yy_navigationBar.userInteractionEnabled = NO;
}

- (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated {
    viewController.yy_navigationBar.userInteractionEnabled = YES;
}

#pragma mark - GestureDelegate

- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer {
    if (self.vcArray.count == 1) {
        return NO;
    } else {
        return YES;
    }
}

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
    return YES;
}

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldBeRequiredToFailByGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
    if (gestureRecognizer == self.screenEdgeGesture || self.panGesture) {
        return YES;
    }
    return NO;
}

#pragma mark - GestureAction

- (void)customNavigationBarAnimation:(UIPanGestureRecognizer *)gesture {
    //    计算（相对于起始位置[手势开始位置]移动了多少个点）/ 屏幕宽度的比例, 用来当做滑动动画的进度
    CGFloat process = [gesture translationInView:self.view].x / self.view.bounds.size.width;
    //    防止值溢出0-1, MIN(1.0, process)确保了值不会大于1.0, MAX(0.0, X)确保了值不小于0.0
    process = MAX(0.0, MIN(1.0, process));
    
    UIGestureRecognizerState state = gesture.state;
    if (state == UIGestureRecognizerStateBegan) {

        [self popStateIsEqualBegin];
    } else if (state == UIGestureRecognizerStateChanged) {
       
        [self popStateIsEqualChangedWithProcess:process];
        
    } else if (state == UIGestureRecognizerStateEnded || state == UIGestureRecognizerStateCancelled) {
        
        [self popStateIsEqualEndedOrCancelledWithProcess:process];
    }
}

- (void)popStateIsEqualBegin {
    self.backType = kYYNavigationBackTypeGesture;
    self.interactivePopTransition = [[UIPercentDrivenInteractiveTransition alloc] init];
    [self popViewControllerAnimated:YES];
    
    self.yy_navigationBar.userInteractionEnabled = NO;
    
    self.previousBar = self.navigationBarStack[self.vcArray.count - 2];
    [self.view insertSubview:self.previousBar belowSubview:self.yy_navigationBar];
    self.previousBar.userInteractionEnabled = NO;
    
    self.previousBarInitialNaviAlpha = self.previousBar.naviAlpha;
    self.naviBarInitialNaviAlpha = self.yy_navigationBar.naviAlpha;
}

- (void)popStateIsEqualChangedWithProcess:(CGFloat)process {
    [self.interactivePopTransition updateInteractiveTransition:process];

    CGFloat tempNaviAlpha = 0;
    CGFloat tempPreviousBarAlpha = 0;

    if (!self.yy_navigationBar.hidden) {
        tempNaviAlpha = MAX(0.0, MIN(self.naviBarInitialNaviAlpha, self.naviBarInitialNaviAlpha - process * 2));
    }
    
    if (!self.previousBar.hidden) {
        tempPreviousBarAlpha = MAX(0.0, MIN(self.naviBarInitialNaviAlpha, self.previousBarInitialNaviAlpha * process));
    }
    
    self.yy_navigationBar.naviAlpha = tempNaviAlpha;
    self.previousBar.naviAlpha = tempPreviousBarAlpha;
}

- (void)popStateIsEqualEndedOrCancelledWithProcess:(CGFloat)process {
    self.yy_navigationBar.userInteractionEnabled = YES;
    self.previousBar.userInteractionEnabled = YES;

    self.previousBar.naviAlpha = self.previousBarInitialNaviAlpha;
    
    [self.previousBar removeFromSuperview];
    self.previousBar = nil;
    
    if (process >= 0.15) {
        [self.vcArray removeLastObject];
        [self.navigationBarStack removeLastObject];
        [self showNavigationBar:self.vcArray.lastObject];
        [self.interactivePopTransition finishInteractiveTransition];
    } else {
        self.yy_navigationBar.naviAlpha = self.naviBarInitialNaviAlpha;
        [self.interactivePopTransition cancelInteractiveTransition];
    }
    
    self.interactivePopTransition = nil;
    self.backType = kYYNavigationBackTypeButton;
}

#pragma mark - GestureAdd/Remove

- (void)addScreenGesture:(UIGestureRecognizer *)gesture {
    self.delegate = self;
    [self.interactivePopGestureRecognizer.view addGestureRecognizer:gesture];
    self.backType = kYYNavigationBackTypeButton;
}

- (void)removeScreenGesture:(UIGestureRecognizer *)gesture {
    [self.interactivePopGestureRecognizer.view removeGestureRecognizer:gesture];
}

#pragma mark - Setter

- (void)setGestureType:(YYNavigationGestureType)gestureType {
    switch (gestureType) {
        case kYYNavigationGestureNoneType:
            [self removeScreenGesture:self.screenEdgeGesture];
            [self removeScreenGesture:self.panGesture];
            break;
        case kYYNavigationGestureScreenEdgeType:
            [self removeScreenGesture:self.panGesture];
            [self addScreenGesture:self.screenEdgeGesture];
            break;
        case kYYNavigationGestureFullScreenType:
            [self removeScreenGesture:self.screenEdgeGesture];
            [self addScreenGesture:self.panGesture];
            break;
    }
}

#pragma mark - Getter

- (NSMutableArray *)navigationBarStack {
    if (!_navigationBarStack) {
        _navigationBarStack = [NSMutableArray array];
    }
    return _navigationBarStack;
}

- (NSMutableArray *)vcArray {
    if (!_vcArray) {
        _vcArray = [NSMutableArray array];
    }
    return _vcArray;
}

- (UIScreenEdgePanGestureRecognizer *)screenEdgeGesture {
    if (!_screenEdgeGesture) {
        _screenEdgeGesture = [[UIScreenEdgePanGestureRecognizer alloc] initWithTarget:self action:@selector(customNavigationBarAnimation:)];
        _screenEdgeGesture.delegate = self;
        _screenEdgeGesture.edges = UIRectEdgeLeft;
    }
    return _screenEdgeGesture;
}

- (UIPanGestureRecognizer *)panGesture {
    if (!_panGesture) {
        _panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(customNavigationBarAnimation:)];
        _panGesture.delegate = self;
    }
    return _panGesture;
}

@end
